import os
import sys
import re
import logging
import logging.config

import ma.const
import ma.log



class FileHandlerRW(object):
    NOT_FOUND = -1
    ERROR = -3
    CUR_POS = -4
    FILE_END = -5
    FILE_START = -6
    
    
    def __init__(self, filename):
        #initialize logger for FileHandlerRW
        self.__log = ma.log.get_logger("ma.fs")
        
        try: 
            self.__log.debug('Creating FileHandlerRW object')
            if os.path.exists(filename):
                self.size = os.stat(filename)[6]
                self.__fd = open(filename, 'r+')
            else:
                self.__fd = open(filename, 'w+')
                self.size = 0
            self.__data = None
        except IOError as err_msg:
            self.__log.error('Unable to successfully create the FileHandlerRW object: %s', err_msg)
            self.__fd = None
    
    
    def seek_first(self, str):
        """function is trying to match a string given in the arguments"""
    
        try:
            if self.__fd == None:
                self.__log.error('Can\'t complete function call: The file in FileHandlerRW object was destroyed')
                return self.ERROR
            else:
                if self.__data == None:
                    self.__log.info('Reading the file in the FileHandlerRW object')
                    self.__data = self.__fd.read()
                self.__log.info('Finding string in the FileHandlerRW file')
                pos = self.__data.find(str, self.__fd.tell())
                if pos == -1:
                    self.__log.debug('String not found in the file')
                    return self.NOT_FOUND
                else:
                    self.__log.debug('String found in the file at location %d', self.__fd.tell())
                    self.__fd.seek(pos, os.SEEK_SET)
                    self.__data = self.__data[pos:]
                    return self.__fd.tell()
        except IOError as err_msg:
            self.__log.error('Unable to seek the file in the FileHandlerRW object: %s', err_msg)
            self.__fd = None
            return self.ERROR
    
            
    def seek_re_first(self, regexp):
        """just like function seek_first this func finds a regular expression
        in a file otherwise logs an error if either file not found or 
        expression not found
        """
        
        try:
            if self.__fd == None:
                self.__log.error('Can\'t complete function call: The file in FileHandlerRW object was destroyed')
                return self.ERROR
            else:
                if self.__data == None:
                    self.__log.info('Reading the file in the FileHandlerRW object')
                    self.__data = self.__fd.read()
                self.__log.info('Finding regular expression in the FileHandlerRW file')
                compiled_regexp = re.compile(regexp, re.IGNORECASE)
                matched_obj = compiled_regexp.search(self.__data)
                if matched_obj == None:
                    self.__log.debug('Regular Expression not found in the file')
                    return self.NOT_FOUND
                else:
                    self.__log.debug('Regular Expression found in the file at location %d', self.__fd.tell())
                    pos = matched_obj.start()
                    self.__fd.seek(pos, os.SEEK_SET)
                    self.__data = self.__data[pos:]
                    return self.__fd.tell()
        except IOError as err_msg:
            self.__log.error('Unable to seek the file in the FileHandlerRW object: %s', err_msg)
            self.__fd = None
            return self.ERROR
    
    
    def seek_forward(self, steps, start = CUR_POS):
        """function moves to a given position in the file in case the given 
        byte offset is not valid it logs an error
        """
                
        try:
            if self.__fd == None:
                self.__log.error('Can\'t complete function call: The file in FileHandlerRW object was destroyed')
                return self.ERROR
            else:
                if start == self.FILE_START:
                    self.__log.debug('Move file pointer in FileHandlerRW object to the start')
                    self.__fd.seek(0, os.SEEK_SET)
                    self.__data = None
                elif start != self.CUR_POS:
                    self.__log.error('Invalid function arg. for start: Valid args CUR_POS, FILE_START')
                    return self.ERROR
                curr_pos = self.__fd.tell()
                if curr_pos + steps > self.size:
                    self.__log.error('Cannot move %d steps ahead. Trying to move over EOF', steps)
                    return self.ERROR
                else:
                    self.__log.info('Moving file pointer in FileHandlerRW %d steps forward', steps)
                    self.__fd.seek(steps, os.SEEK_CUR)
                    if self.__data != None:
                        self.__data = self.__data[steps:]
                return curr_pos + steps
        except IOError as err_msg:
            self.__log.error('Unable to seek the file in the FileHandlerRW object: %s', err_msg)
            self.__fd = None
            return self.ERROR


    def write_replace(self, write_data, replace_no_chars, start = CUR_POS):
        """func tries to overwrite a no of charac (replace_no_chars) from a 
        specified cursor position (start) given in the arguments... the func 
        logs an error if either file not found or cursor position not valid 
        for file length or data to be replaced exceeds file length  
        """
        
        try:
            if self.__fd == None:
                self.__log.error('Can\'t complete function call: The file in FileHandlerRW object was destroyed')
                return self.ERROR
            else:
                # seek the location where the data needs to be written
                if start >= 0:
                    if start > self.size + 1:
                        # if someone tries to write even after end of the data
                        self.__log.error('Start argument ahead of EOF')
                        return self.ERROR
                    else:
                        self.__log.debug('Moving file pointer %d steps from start of file', start)
                        self.__fd.seek(start, os.SEEK_SET)
                else:
                    if start != self.CUR_POS:
                        self.__log.error('Invalid function arg. for start: Valid args CUR_POS, start >= 0 for moving from file start')
                        return self.ERROR
                # check if from the current position, we have the said no. of 
                #    replaceable characters left
                if replace_no_chars > self.size - self.__fd.tell():
                    self.__log.error('Less than %d replaceable characters remaining from the position %d', [replace_no_chars, self.__fd.tell()])
                    return self.ERROR
                
                self.__log.debug('Clearing the data buffer')
                self.__data = None
                # write data
                if len(write_data) == replace_no_chars:
                    self.__log.info('Over-writing %d bytes', replace_no_chars)
                    self.__fd.write(write_data)
                else:
                    write_pos = self.__fd.tell()
                    # copy the data from the position ahead
                    self.__log.debug('Copying forward portion of the file')
                    self.__fd.seek(replace_no_chars, os.SEEK_CUR)
                    front_data = self.__fd.read()
                    # move back the pointer to the writing position
                    self.__log.debug('Move back file pointer to the location where data needs to be over-written')
                    self.__fd.seek(write_pos, os.SEEK_SET)
                    
                    # write the user data and data ahead
                    self.__log.info('Over-writing %d bytes', replace_no_chars)
                    self.__fd.write(write_data)
                    self.__log.info('Copying forward portion of the file')
                    self.__fd.write(front_data)
                # adjust the current size of the file and then flush the data 
                self.size += len(write_data) - replace_no_chars
                self.__log.info('Flushing file for writing')
                self.__fd.flush()
        except IOError as err_msg:
            self.__log.error('Unable to write replace in the file in the FileHandlerRW object: %s', err_msg)
            self.__fd = None
            return self.ERROR

    
    def write_append(self, write_data):
        try:
            if self.__fd == None:
                self.__log.error('Can\'t complete function call: The file in FileHandlerRW object was destroyed')
                return self.ERROR
            else:
                self.__log.debug('Seeking to end of file')
                self.__fd.seek(1, os.SEEK_END)
                self.__log.debug('Clearing the data buffer')
                self.__data = None
                self.__log.info('Appending %d bytes', len(write_data))
                self.__fd.write(write_data)
                # adjust the current size of the file and then flush the data 
                self.size += len(write_data)
                self.__log.info('Flushing file for writing')
                self.__fd.flush()
        except IOError as err_msg:
            self.__log.error('Unable to write append in the file in the FileHandlerRW object: %s', err_msg)
            self.__fd = None
    
    def __del__(self):
        try:
            if self.__fd != None:
                self.__log.info('Destructor: Closing %s file in FileHandlerRW', self.__fd.name)
                self.__fd.close()
        except IOError as err_msg:
            self.__log.error('Unable to successfully destroy the FileHandlerRW object: %s', err_msg)
            self.__fd = None
            return 1


if __name__ == '__main__':
    """main function for testing"""
    print("testing filehndl module")
    fhrw = FileHandlerRW('test.txt')
    # find xml tag 
    print(fhrw.seek_re_first('<[a-z]*>'))
    # find xml tag end
    start_pos = fhrw.seek_re_first('>')
    # find closing tag
    end_pos = fhrw.seek_re_first('</')
    